﻿using System;
using System.Data;
using System.Data.Entity;
using System.Diagnostics.Contracts;
using System.Linq;
using UnitOfWork.Interfaces;

namespace UnitOfWork.EntityFramework
{
	public abstract class UnitOfWorkBase : DbContext,  IUnitOfWork
	{
		protected internal UnitOfWorkBase(string nameOrConnectionString) : base(nameOrConnectionString)
		{
		}

		public IQueryable<TEntity> Query<TEntity>() where TEntity : class
		{
			return GetEntities<TEntity>();
		}

		public void RegisterNew<TEntity>(TEntity entity) where TEntity : class
		{
			GetEntities<TEntity>().Add(entity);
		}
		public void RegisterUnchanged<TEntity>(TEntity entity) where TEntity : class
		{
			GetEntities<TEntity>().Attach(entity);
		}
		public void RegisterChanged<TEntity>(TEntity entity) where TEntity : class
		{
			GetEntities<TEntity>().Attach(entity);
			if (ChangeTracker != null)
			{
				var entries = ChangeTracker.Entries<TEntity>();
				if (entries != null)
				{
					var entityEntry = entries.First(entry => ReferenceEquals(entity, entry.Entity));
					if (entityEntry != null)
						entityEntry.State = EntityState.Modified;
					else
						throw new InvalidOperationException("This entity is not tracked, cannot register as Changed");
				}
				else
					throw new InvalidOperationException(typeof(TEntity).Name + "is not tracked, cannot register as Changed");
			}
			else 
				throw new InvalidOperationException("Change tracker disabled, cannot register as Changed");
		}
		public void RegisterDeleted<TEntity>(TEntity entity) where TEntity : class 
		{
			GetEntities<TEntity>().Remove(entity);
		}
		public void Save()
		{
			SaveChanges();
		}

		protected IDbSet<TEntity> GetEntities<TEntity>() where TEntity : class
		{
			Contract.Ensures(Contract.Result<IDbSet<TEntity>>() != null);
			var entities = Set<TEntity>();
			if (entities == null)
				throw new InvalidOperationException("No entity of type " + typeof(TEntity).Name);
			return entities;
		}
	}
}
